package cn.buddha.jesj.thread.tl;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * @author shihao
 * @description threadLocal demo
 */
@Slf4j
public class ThreadLocalTest {

    public static void main(String[] args) {
        try {
            threadLocalTest();

            itlNormal();

            itlBugOfExecutor();
        } catch (InterruptedException e) {
            log.error("呵呵", e);
        }
    }

    public static void threadLocalTest() {
        log.info("========== threadLocalThread starting...");

        ThreadLocal<Object> threadLocal = new ThreadLocal<>();
        threadLocal.set("呕吼，主线程哈");
        log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("sh-pool-%d").build();
        ExecutorService threadPool = new ThreadPoolExecutor(2, 4,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

        try {
            CountDownLatch countDownLatch = new CountDownLatch(2);

            threadPool.execute(() -> {
                log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
                threadLocal.set("子线程1");
                log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
                threadLocal.remove();

                countDownLatch.countDown();
            });

            threadPool.execute(() -> {
                threadLocal.set("子线程2");
                log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
                threadLocal.remove();

                countDownLatch.countDown();
            });

            try {
                countDownLatch.await();
            } catch (InterruptedException ignored) {
            }
            log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
        } finally {
            threadPool.shutdown();
        }

        log.info("========== threadLocalThread end");
    }

    /**
     * 手动创建线程时，父子线程之间的数据传递。这种情况下就是正常的。
     */
    public static void itlNormal() throws InterruptedException {
        log.info("========== itlNormal starting...");

        ThreadLocal<Object> threadLocal = new InheritableThreadLocal<>();
        threadLocal.set("《水浒传》");

        log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

        CountDownLatch mainCountDownLatch = new CountDownLatch(2);

        new Thread(() -> {
            log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
            threadLocal.set("及时雨宋江");
            log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

            mainCountDownLatch.countDown();
        }).start();

        // 休眠一秒钟，模仿子线程线程串行的效果。当然，真正的串行就需要用到线程编排了。譬如说，京东的 asyncTool。
        TimeUnit.SECONDS.sleep(1);

        new Thread(() -> {
            log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
            threadLocal.set("豹子头林冲");
            log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

            mainCountDownLatch.countDown();
        }).start();

        try {
            mainCountDownLatch.await();
        } catch (InterruptedException ignored) {
        }
        log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

        log.info("========== itlNormal end");
    }

    /**
     * InheritableThreadLocal 在线程池中的 bug
     */
    public static void itlBugOfExecutor() throws InterruptedException {
        log.info("========== itlBugOfExecutor starting...");

        ThreadLocal<Object> threadLocal = new InheritableThreadLocal<>();
        threadLocal.set("《三国演义》");

        log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

        /**
         * 注意，这里创建的线程池中只有一个线程。创建的线程太多，不太容易看到我们想要的结果。
         */
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        try {
            CountDownLatch countDownLatch = new CountDownLatch(2);

            Runnable runnable = () -> {
                log.info("sub thread starting execute ...");

                /**
                 * 由于线程池中只有一个线程，那么在该线程第二次执行该任务runnable时，
                 * 这里是已经拿不到父线程要传递给子线程的值了，因为在下面一行 threadLocal.set() 就会把原本父线程的值给覆盖掉。
                 */
                log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());
                threadLocal.set("既生瑜何生亮");
                log.info("[{}] threadLocal value is: [{}]", Thread.currentThread().getName(), threadLocal.get());

                log.info("sub thread end");
                countDownLatch.countDown();
            };

            executorService.execute(runnable);
            // 休眠一秒钟，模仿子线程线程串行的效果。当然，真正的串行就需要用到线程编排了。譬如说，京东的 asyncTool。
            TimeUnit.SECONDS.sleep(1);
            executorService.execute(runnable);

            countDownLatch.await();
        } finally {
            executorService.shutdown();
        }

        log.info("========== itlBugOfExecutor end");
    }

}
